package com.avengers.core.timer;


import com.avengers.core.bean.AvengersSerialLock;
import com.avengers.core.bean.Numbers;
import com.avengers.core.bean.TimerClassInfo;
import com.avengers.core.bean.enums.SymbolEnum;
import com.avengers.core.kit.SleepKit;
import com.avengers.core.kit.StringKit;
import com.avengers.core.kit.TimeKit;
import com.avengers.job.JobNoKit;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

/**
 * @author yu.wang
 * @since 2025/4/16 16:49
 **/
public abstract class BaseLockTimer implements Job {
    private final String jobNo;
    private final String jobName;
    /**
     * 是否等待锁执行
     * true: 如果拿不到锁，则等待锁，当前类仅一个线程等待，后进入线程则直接跳过执行
     * false: 拿不到锁直接放弃
     */
    private final boolean runWaitLock;

    private static TimerClassInfo RUNNING_TIMER;
    private static final Map<String, Lock> CLASS_MAP = new HashMap<>();
    private static final Map<String, TimerClassInfo> WAIT_CLASS_INFO_MAP = new HashMap<>();

    public BaseLockTimer(String jobName) {
        this(jobName, false);
    }

    public BaseLockTimer(String jobName, boolean runAfterHold) {
        this(jobName, runAfterHold, null);
    }

    public BaseLockTimer(String jobName, boolean runAfterHold, String jobNo) {
        this.jobNo = jobNo;
        this.jobName = jobName;
        this.runWaitLock = runAfterHold;
    }

    protected String getAndCreateJobNo() {
        return JobNoKit.create("JOB_");
    }

    @Override
    public final void execute(JobExecutionContext jobExecutionContext) {
        execute(jobExecutionContext.getJobDetail().getKey().getName());
    }

    public final TimerClassInfo execute(Class<? extends BaseLockTimer> targetClass) {
        return execute(targetClass.getName());
    }

    private TimerClassInfo execute(String currentClassName) {
        String finalJobNo = StringKit.notBlank(this.jobNo) ? this.jobNo : getAndCreateJobNo();
        Logger logger = logger();
        AvengersSerialLock realRunTimerLock = getLock();
        String lockSerialNo = realRunTimerLock.getSerialNo();
        boolean getRunTimerLockSuccess = false;
        try {
            // 未获取到锁，需要等待锁
            Lock waitLockClassLock = CLASS_MAP.get(currentClassName);
            if (null == waitLockClassLock) {
                synchronized (currentClassName.intern()) {
                    waitLockClassLock = CLASS_MAP.computeIfAbsent(currentClassName, k -> new ReentrantLock());
                }
            }
            waitLockClassLock.lock();
            try {
                getRunTimerLockSuccess = realRunTimerLock.tryLock();
                if (getRunTimerLockSuccess) {
                    // 获取到锁，开始执行
                    RUNNING_TIMER = new TimerClassInfo(finalJobNo, this.jobName, currentClassName);
                } else {
                    if (!this.runWaitLock) {
                        // 未获取到锁，直接跳过执行
                        SleepKit.sleep(Numbers.INT_100.longValue());
                        skipLog(lockSerialNo, RUNNING_TIMER, TRUE);
                        return RUNNING_TIMER;
                    }
                    // 首次获取锁失败，需要校验当前线程是否需要等待锁
                    TimerClassInfo waitClassInfo = WAIT_CLASS_INFO_MAP.get(currentClassName);
                    if (null != waitClassInfo) {
                        // 当前类已经在等待锁
                        skipLog(lockSerialNo, waitClassInfo, FALSE);
                        return RUNNING_TIMER;
                    }
                    if (null != RUNNING_TIMER && currentClassName.equals(RUNNING_TIMER.getClassName())) {
                        // 当前类已经在执行
                        skipLog(lockSerialNo, RUNNING_TIMER, TRUE);
                        return RUNNING_TIMER;
                    }
                    WAIT_CLASS_INFO_MAP.put(currentClassName, new TimerClassInfo(finalJobNo, null, currentClassName));
                }
            } finally {
                waitLockClassLock.unlock();
            }
            if (!getRunTimerLockSuccess) {
                logger.info("LockTimer {} =====> {} get lock begin...", lockSerialNo, jobName);
                TimeKit.logUsedTime(realRunTimerLock::lock, logger, "LockTimer {} >>>>>> {} get lock", lockSerialNo, jobName);
                WAIT_CLASS_INFO_MAP.remove(currentClassName);
                getRunTimerLockSuccess = TRUE;
            }
            logger.info("LockTimer {} >>>>>>【{}】begin......", lockSerialNo, jobName);
            TimeKit.logUsedTime(this::realExecute, logger, "LockTimer {} <<<<<<【{}】over", lockSerialNo, jobName);
        } finally {
            if (getRunTimerLockSuccess) {
                RUNNING_TIMER = null;
                realRunTimerLock.unlock();
            }
            JobNoKit.clear();
        }
        return RUNNING_TIMER;
    }

    private void skipLog(String lockSerialNo, TimerClassInfo runningTimer, boolean isRunning) {
        String runningKeyword = isRunning ? "RUNNING" : "WAIT";
        if (null == runningTimer) {
            logger().info("LockTimer {} <<<<< {} skip by {} this time <<<<<", lockSerialNo, jobName, runningKeyword);
            return;
        }
        SleepKit.sleep(Numbers.INT_100.longValue());
        BigDecimal useTimeMillisecond = TimeKit.useTimeMillisecond(runningTimer.getStartNanoTime());
        String useTimeStr = TimeKit.useTimeStr(useTimeMillisecond.longValue());
        logger().info("LockTimer {} <<<<< {} skip by {} {} running {}ms({}) <<<<<", lockSerialNo, jobName, runningKeyword, runningTimer, useTimeMillisecond, StringKit.isBlank(useTimeStr) ? SymbolEnum.empty.string : String.format("(%s)", useTimeStr));
    }

    /**
     * 逻辑处理
     */
    protected abstract void realExecute();

    /**
     * 获取同步锁，保证该任务同时只能运行一个
     */
    protected abstract AvengersSerialLock getLock();

    /**
     * 日志
     */
    protected abstract Logger logger();
}
